// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 包测试为Go包的自动测试提供支持。
// 它旨在与“go test”命令配合使用，该命令自动执行
// 任何形式的函数
// func TestXxx（*testing.T）
// 其中Xxx不以小写字母开头。函数名
// 用于标识测试例程。
// 
// 在这些函数中，使用错误、失败或相关方法发出失败信号。
// 
// 要编写一个新的测试套件，请创建一个名称以_test结尾的文件。确保
// 包含此处所述的TestXxx函数。将该文件放入与被测试文件相同的
// 包中。该文件将从常规
// 包生成中排除，但将在运行“go test”命令时包括在内。
// 有关详细信息，请运行“go help test”和“go help testflag”。
// 
// 一个简单的测试函数如下所示：
// 
// func TestAbs（t*testing.t）{
// got:=Abs（-1）
// if got！=1{
// t.Errorf（“Abs 1）=%d；want 1”，got）
// 
// }
// 
// Benchmarks 
// 
// /形式为
// func BenchmarkXxx（*testing.B）
// 的函数被视为基准，在提供
// its-bench标志时，由“go test”命令执行。基准测试是按顺序运行的。
// 
// 有关测试标志的说明，请参阅
// https:
// 
// 一个示例基准函数如下所示：
// func BenchmarkRandInt（b*Testing.b）{
// 对于i:=0；i<b.N；i++{
// rand.Int（）
// }
// }
// 
// 基准函数必须运行目标代码b.N次。
// 在基准执行过程中，b.N会被调整，直到基准函数持续
// 足够长的时间以可靠计时。输出
// BenchmarkRandInt-8 68453040 17.8 ns/op 
// 表示循环以每循环17.8 ns的速度运行68453040次。如果基准测试在运行前需要一些昂贵的设置，计时器可能会被重置：
// 
// func BenchmarkBigLen（b*testing.b）{
// big:=NewBig（）
// b.ResetTimer（）
// for i:=0；i<b.N；i++{
// /big.Len（）
// }
// }
// 
// 如果一个基准需要在并行设置中测试性能，它可以使用
// RunParallel helper函数；这些基准打算与
// go test-cpu标志一起使用：
// 
// func BenchmarkTemplateParallel（b*testing.b）{
// temp:=template.Must（template.New（“test”）.Parse（“Hello，{}}}！）
// b.RunParallel（func（pb*testing.pb）{
// var buf bytes.Buffer 
// for pb.Next（）{
// buf.Reset（）
// 模板执行（&buf，“世界”）
// /}
// /}）
// /}
// 
// 示例
// 
// 该包还运行并验证示例代码。示例函数可能包含以“Output:”开头的结束行注释，并与运行测试时函数的标准输出
// 进行比较。（比较
// 忽略前导和尾随空格。）以下是示例的示例：
// 
// func ExampleHello（）{
// /fmt.Println（“hello”）
// 
// 
// 
// func examplesaholations（）{
// fmt.Println（“你好，和”）
// /fmt.Println（“再见”）
// /输出：
// /你好，和
// /再见
// 
// 
// 注释前缀“无序输出：”类似于“输出：”，但与任何
// 行顺序：
// 
// func ExamplePerm（）{
// for u，value:=范围Perm（5）{
// fmt.Println（value）
// 
// /无序输出：4 
// /2 
// /1 
// /3 
// /0 
// }
// /
// 编译但不执行没有输出注释的示例函数。
// 
// 为包、函数F、类型T和
// 类型T上的方法M声明示例的命名约定是：
// 
// func Example（）{…}
// func Example（）{…}
// func Example（）{…}
// func ExampleT_M（）{…}
// 
// 一个包/类型/函数/方法的多个示例函数可能由
// 在名称后面附加一个不同的后缀提供。后缀必须以
// 小写字母开头。
// 
// func示例_后缀（）{…}
// func示例_后缀（）{…}
// func示例_后缀（）{…}
// func示例_后缀（）{…}
// 
// 当整个测试文件包含单个
// 示例函数、至少一个其他函数、类型、变量或常量
// 声明，并且没有测试或基准函数时，整个测试文件作为示例显示。
// 
// 跳过
// 
// 调用
// 可以在运行时跳过测试或基准测试*T或*B的Skip方法：
// 
// 函数测试耗时（T*testing.T）{
// if testing.Short（）{
// t.Skip（“在短模式下跳过测试”）
// }
// …
// }
// 
// 子测试和子基准
// 
// T和B的运行方法允许定义子测试和子基准，
// 而不必为每个子测试和子基准定义单独的函数。这允许使用
// 类似于表驱动基准并创建分层测试sts.
// 它还提供了一种共享常见设置和拆卸代码的方法：
// 
// func TestFoo（t*testing.t）{
// <setup code>
// /t.Run（“a=1”，func（t*testing.t）{…}）
// t.Run（“B=1”，func（t*testing.t）{…}）
// <拆卸代码>
// }
// 
// 每个子测试和子基准都有一个唯一的名称：顶级测试的名称
// 和传递给运行的名称序列的组合，由
// 斜杠分隔，带有用于消除歧义的可选尾随序列号。
// 
// -run和-bench命令行标志的参数是一个与测试名称匹配的未编排的正则表达式。对于具有多个斜杠分隔的
// 元素（如子测试）的测试，参数本身是斜杠分隔的，
// 表达式依次匹配每个名称元素。因为例如，使用“matching”表示“其名称包含”：
// 
// go test-run“”#运行所有测试。
// go test-run Foo#运行与“Foo”匹配的顶级测试，如“TestFooBar”.
// go test-run Foo/A=#对于匹配“Foo”的顶级测试，运行匹配“A=”.
// go test-run/A=1#对于所有顶级测试，运行匹配“A=1”的子测试.
// 
// 子测试也可用于控制并行性。父测试只有在其所有子测试完成后才能完成
// 在本例中，所有测试都是
// 彼此并行运行，并且仅彼此并行运行，而与
// 其他顶级测试无关t可以定义为：
// 
// func TestGroupedParallel（t*testing.t）{
// for u，tc:=范围测试{
// tc:=tc
// t.Run（tc.Name，func（t*testing.t）{
// /…
// /}）
// /}
// /}
// 
// 如果程序超过8128个并发goroutine，
// 则竞赛检测器将终止程序，因此在使用-race标志集运行并行测试时请小心。
// 
// 在并行子测试完成之前，Run不会返回，这提供了一种方法
// 在一组并行测试之后进行清理：
// 
// func TestTeardownParallel（t*testing.t）{
// /在并行测试完成之前，此运行不会返回。
// t.Run（“group”，func（t*testing.t）{
// /t.Run（“Test1”，parallelTest1）
// t.Run（“Test2”，parallelTest2）
// /}）
// /<拆卸代码>
// /}
// 
// Main 
// 
// 测试或基准测试程序有时需要在执行之前或之后进行额外的设置或拆卸
// 有时还需要控制
// 在主线程上运行的代码。以支持这些和其他cases，
// 如果测试文件包含函数：
// 
// func TestMain（m*testing.m）
// 
// 则生成的测试将调用TestMain（m）而不是直接运行测试或基准测试。TestMain在主goroutine中运行，可以执行任何设置
// 并且在调用m.Run.m时需要卸载。Run将返回一个exit 
// 代码，该代码可以传递给os.exit。如果TestMain返回，测试包装
// 将l将m.Run的结果传递给os.Exit本身。
// 
// 调用TestMain时，flag.Parse尚未运行。如果TestMain依赖于
// 命令行标志，包括测试包的标志，则应调用
// flag.Parse显式解析。命令行标志始终按时间解析test 
// 或运行基准函数。
// 
// TestMain的一个简单实现是：
// 
// func TestMain（m*testing.m）{
// 
// os.Exit（m.run（））
// 
// TestMain是一个低级原语，对于普通测试函数足够的临时
// 测试需求来说不应该是必需的。
package testing

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"internal/race"
	"io"
	"math/rand"
	"os"
	"runtime"
	"runtime/debug"
	"runtime/trace"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"unicode"
	"unicode/utf8"
)

var initRan bool

// Init注册测试标志。这些标志由
// 自动注册“go test”在运行测试函数之前使用命令，因此在调用诸如Benchmark之类的函数时，不使用“go test”只需要使用Init.
// 
// Init如果已经被调用则无效。
func Init() {
	if initRan {
		return
	}
	initRan = true
	// short标志要求测试运行得更快，但其功能
	// 是由测试编写者自己提供的。测试包只是其
	// 主页。all.bash安装脚本将其设置为使安装更加高效，但默认情况下该标志是关闭的，因此是一个简单的“go test”将对包执行完整测试。失败快速标志请求在第一次测试失败后停止测试执行。失败快速标志请求在其中创建配置文件等的目录。从失败快速标志运行时，转到测试，二进制文件始终在包的源目录中运行；
	short = flag.Bool("test.short", false, "run smaller test suite to save time")

	failFast = flag.Bool("test.failfast", false, "do not start new tests after the first test failure")

	// 此标志允许“go test”告诉二进制文件在
	// 的“go test”所在的目录中写入文件命令正在运行。
	outputDir = flag.String("test.outputdir", "", "write profiles to `dir`")
	// 在测试运行时进行报告；默认值为成功无提示。
	chatty = flag.Bool("test.v", false, "verbose: print additional output")
	count = flag.Uint("test.count", 1, "run tests and benchmarks `n` times")
	coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to `file`")
	matchList = flag.String("test.list", "", "list tests, examples, and benchmarks matching `regexp` then exit")
	match = flag.String("test.run", "", "run only tests and examples matching `regexp`")
	memProfile = flag.String("test.memprofile", "", "write an allocation profile to `file`")
	memProfileRate = flag.Int("test.memprofilerate", 0, "set memory allocation profiling `rate` (see runtime.MemProfileRate)")
	cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to `file`")
	blockProfile = flag.String("test.blockprofile", "", "write a goroutine blocking profile to `file`")
	blockProfileRate = flag.Int("test.blockprofilerate", 1, "set blocking profile `rate` (see runtime.SetBlockProfileRate)")
	mutexProfile = flag.String("test.mutexprofile", "", "write a mutex contention profile to the named file after execution")
	mutexProfileFraction = flag.Int("test.mutexprofilefraction", 1, "if >= 0, calls runtime.SetMutexProfileFraction()")
	panicOnExit0 = flag.Bool("test.paniconexit0", false, "panic on call to os.Exit(0)")
	traceFile = flag.String("test.trace", "", "write an execution trace to `file`")
	timeout = flag.Duration("test.timeout", 0, "panic test binary after duration `d` (default 0, timeout disabled)")
	cpuListStr = flag.String("test.cpu", "", "comma-separated `list` of cpu counts to run each test with")
	parallel = flag.Int("test.parallel", runtime.GOMAXPROCS(0), "run at most `n` tests in parallel")
	testlog = flag.String("test.testlogfile", "", "write test action log to `file` (for use only by cmd/go)")
	shuffle = flag.String("test.shuffle", "off", "randomize the execution order of tests and benchmarks")

	initBenchmarkFlags()
}

var (
	// 标志，在初始化期间注册。
	short                *bool
	failFast             *bool
	outputDir            *string
	chatty               *bool
	count                *uint
	coverProfile         *string
	matchList            *string
	match                *string
	memProfile           *string
	memProfileRate       *int
	cpuProfile           *string
	blockProfile         *string
	blockProfileRate     *int
	mutexProfile         *string
	mutexProfileFraction *int
	panicOnExit0         *bool
	traceFile            *string
	timeout              *time.Duration
	cpuListStr           *string
	parallel             *int
	shuffle              *string
	testlog              *string

	haveExamples bool // 是否有示例？

	cpuList     []int
	testlogFile *os.File

	numFailed uint32 // 测试失败次数
)

type chattyPrinter struct {
	w          io.Writer
	lastNameMu sync.Mutex // 卫士姓氏
	lastName   string     // 聊天模式下最后打印的测试名
}

func newChattyPrinter(w io.Writer) *chattyPrinter {
	return &chattyPrinter{w: w}
}

// Updatef将有关命名测试状态的消息打印到w.
// 
// 格式化的消息必须包含测试名称本身。
func (p *chattyPrinter) Updatef(testName, format string, args ...interface{}) {
	p.lastNameMu.Lock()
	defer p.lastNameMu.Unlock()

	// 由于该消息已经暗示与特定的新测试关联，
	// 我们不需要检查旧测试名称是什么或记录一个extra CONT line 
	// 用于它。（我们正在更新它，当前消息已经包含了
	// 测试名称。）
	p.lastName = testName
	fmt.Fprintf(p.w, format, args...)
}

// Printf打印一条由命名测试生成的消息，该消息不一定提及该测试的名称本身。
func (p *chattyPrinter) Printf(testName, format string, args ...interface{}) {
	p.lastNameMu.Lock()
	defer p.lastNameMu.Unlock()

	if p.lastName == "" {
		p.lastName = testName
	} else if p.lastName != testName {
		fmt.Fprintf(p.w, "=== CONT  %s\n", testName)
		p.lastName = testName
	}

	fmt.Fprintf(p.w, format, args...)
}

// 跳过
const maxStackLen = 50

// common保存T和B之间的公共元素，
// 捕获诸如Errorf之类的公共方法。
type common struct {
	mu          sync.RWMutex         // 保护这组字段
	output      []byte               // 由测试或基准生成的输出。
	w           io.Writer            // 适用于flushToParent。
	ran         bool                 // 已执行测试或基准测试（或其子测试之一）。
	failed      bool                 // 测试或基准测试失败。
	skipped     bool                 // 已跳过测试或基准测试。
	done        bool                 // 测试已完成，所有子测试均已完成。
	helperPCs   map[uintptr]struct{} // 写入文件/行信息时要跳过的函数
	helperNames map[string]struct{}  // 转换为函数名的HelperPC 
	cleanups    []func()             // 测试结束时要调用的可选函数
	cleanupName string               // 清除函数的名称。
	cleanupPc   []uintptr            // 调用清理点处的堆栈跟踪。
	finished    bool                 // 测试功能已完成。

	chatty     *chattyPrinter // 如果设置了chatty标志，则为chattyPrinter的副本。
	bench      bool           // 当前测试是否为基准测试。
	hasSub     int32          // 以原子方式编写。
	raceErrors int            // 测试期间检测到的种族数。
	runner     string         // 运行测试的tRunner的函数名。

	parent   *common
	level    int       // 测试或基准的嵌套深度。
	creator  []uintptr // 如果级别>0，则父级调用t.Run的点处的堆栈跟踪。
	name     string    // 测试或基准的名称。
	start    time.Time // 时间测试或基准测试已启动
	duration time.Duration
	barrier  chan bool // 向可能启动的并行子测试发出信号。
	signal   chan bool // 发出测试完成的信号。
	sub      []*T      // 要并行运行的子测试队列。

	tempDirMu  sync.Mutex
	tempDir    string
	tempDirErr error
	tempDirSeq int32
}

// Short报告是否设置了-test.Short标志。
func Short() bool {
	if short == nil {
		panic("testing: Short called before Init")
	}
	// 捕获从TestMain调用此函数而不首先调用flag.Parse的代码。
	if !flag.Parsed() {
		panic("testing: Short called before Parse")
	}

	return *short
}

// CoverMode报告测试覆盖模式设置为什么。
// 值为“设置”、“计数”或“原子”。如果未启用测试覆盖率，则返回值将为
// 空。
func CoverMode() string {
	return cover.Mode
}

// 详细报告是否设置了-test.v标志。
func Verbose() bool {
	// 简言之相同。
	if chatty == nil {
		panic("testing: Verbose called before Init")
	}
	if !flag.Parsed() {
		panic("testing: Verbose called before Parse")
	}
	return *chatty
}

// frameSkip在未标记为帮助程序的函数中从跳过帧后开始搜索第一个调用方帧
// 并返回该帧。
// 如果搜索到一个tRunner函数，该函数是测试的入口点，而该测试不是子测试，则搜索将停止。
// 必须在保持c.mu的情况下调用此函数。
func (c *common) frameSkip(skip int) runtime.Frame {
	// 如果搜索继续进入父测试，我们将不得不暂时保留其mu。如果我们返回，我们需要解锁它。
	shouldUnlock := false
	defer func() {
		if shouldUnlock {
			c.mu.Unlock()
		}
	}()
	var pc [maxStackLen]uintptr
	// 跳过两个额外的帧来说明此函数
	// 和runtime.Callers本身。
	n := runtime.Callers(skip+2, pc[:])
	if n == 0 {
		panic("testing: zero callers found")
	}
	frames := runtime.CallersFrames(pc[:n])
	var firstFrame, prevFrame, frame runtime.Frame
	for more := true; more; prevFrame = frame {
		frame, more = frames.Next()
		if frame.Function == c.cleanupName {
			frames = runtime.CallersFrames(c.cleanupPc)
			continue
		}
		if firstFrame.PC == 0 {
			firstFrame = frame
		}
		if frame.Function == c.runner {
			// 我们一直到tRunner调用测试函数
			// 为止（因此用户必须让
			// 从该测试函数内部调用tb.Helper）。
			// 如果这是一个顶级测试，只跳到测试函数本身。
			// 如果我们在子测试中，请继续在父测试中搜索，
			// 从创建此子测试的调用Run的点开始搜索。
			if c.level > 1 {
				frames = runtime.CallersFrames(c.creator)
				parent := c.parent
				// 在这一点之后，我们不再看当前的c，
				// 所以我们应该解锁它的mu，除非它是原始接收器，
				// 在这种情况下，我们的调用者不希望我们这样做。
				if shouldUnlock {
					c.mu.Unlock()
				}
				c = parent
				// 当我们不再需要c.mu时，记得解锁它，要么因为我们提升了另一个嵌套级别，要么因为我们返回了。
				shouldUnlock = true
				c.mu.Lock()
				continue
			}
			return prevFrame
		}
		// 如果自上次执行转换后添加了更多帮助程序PC 
		if c.helperNames == nil {
			c.helperNames = make(map[string]struct{})
			for pc := range c.helperPCs {
				c.helperNames[pcToName(pc)] = struct{}{}
			}
		}
		if _, ok := c.helperNames[frame.Function]; !ok {
			// 发现一个不在帮助程序函数内的帧。
			return frame
		}
	}
	return firstFrame
}

// 装饰在字符串前面加上调用站点的文件和行
// 并在需要时插入最后的换行符和缩进空格进行格式化。
// 必须在保持c.mu的情况下调用此函数。
func (c *common) decorate(s string, skip int) string {
	frame := c.frameSkip(skip)
	file := frame.File
	line := frame.Line
	if file != "" {
		// 在最后一个文件名分隔符处截断文件名。
		if index := strings.LastIndex(file, "/"); index >= 0 {
			file = file[index+1:]
		} else if index = strings.LastIndex(file, "\\"); index >= 0 {
			file = file[index+1:]
		}
	} else {
		file = "???"
	}
	if line == 0 {
		line = 1
	}
	buf := new(strings.Builder)
	// 每行缩进至少4个空格。
	buf.WriteString("    ")
	fmt.Fprintf(buf, "%s:%d: ", file, line)
	lines := strings.Split(s, "\n")
	if l := len(lines); l > 1 && lines[l-1] == "" {
		lines = lines[:l-1]
	}
	for i, line := range lines {
		if i > 0 {
			// 第二行和后续行缩进了额外的4个空格。
			buf.WriteString("\n        ")
		}
		buf.WriteString(line)
	}
	buf.WriteByte('\n')
	return buf.String()
}

// flushToParent在使用给定的格式和参数首次写入头
// 后，将c.output写入父级。但是为什么呢？
func (c *common) flushToParent(testName, format string, args ...interface{}) {
	p := c.parent
	p.mu.Lock()
	defer p.mu.Unlock()

	c.mu.Lock()
	defer c.mu.Unlock()

	if len(c.output) > 0 {
		format += "%s"
		args = append(args[:len(args):len(args)], c.output)
	}

	if c.chatty != nil && p.w == c.chatty.w {
		// 我们正在刷新到实际输出，因此请跟踪此输出是否与特定测试关联（特别是，下一个输出是否与该测试关联）。
		// 无论是humans还是cmd/test2json都无法轻松解析这些内容。
		// （请参阅https:
		c.chatty.Updatef(testName, format, args...)
	} else {
		// 我们正在刷新父测试的输出缓冲区，当它最终刷新到stdout时，它将跟随一个测试名称头。
		fmt.Fprintf(p.w, format, args...)
	}
}

type indenter struct {
	c *common
}

func (w indenter) Write(b []byte) (n int, err error) {
	n = len(b)
	for len(b) > 0 {
		end := bytes.IndexByte(b, '\n')
		if end == -1 {
			end = len(b)
		} else {
			end++
		}
		// 4个空格的缩进将使虚线与父测试的状态
		// 指示器对齐。
		const indent = "    "
		w.c.output = append(w.c.output, indent...)
		w.c.output = append(w.c.output, b[:end]...)
		b = b[end:]
	}
	return
}

// fmtDuration返回一个表示d的字符串，格式为“87.00s”.
func fmtDuration(d time.Duration) string {
	return fmt.Sprintf("%.2fs", d.Seconds())
}

// TB是T和B的通用接口。
type TB interface {
	Cleanup(func())
	Error(args ...interface{})
	Errorf(format string, args ...interface{})
	Fail()
	FailNow()
	Failed() bool
	Fatal(args ...interface{})
	Fatalf(format string, args ...interface{})
	Helper()
	Log(args ...interface{})
	Logf(format string, args ...interface{})
	Name() string
	Setenv(key, value string)
	Skip(args ...interface{})
	SkipNow()
	Skipf(format string, args ...interface{})
	Skipped() bool
	TempDir() string

	// 是一种私有方法，用于防止用户实现
	// 接口，因此将来添加到该接口中不会违反Go 1兼容性。
	private()
}

var _ TB = (*T)(nil)
var _ TB = (*B)(nil)

// T是传递给测试函数的类型，用于管理测试状态和支持表单尝试的测试日志。
// 
// 当测试函数返回或调用任何方法
// FailNow、Fatal、Fatalf、SkipNow、Skip或Skipf时，测试结束。这些方法以及
// 并行方法只能从运行
// 测试函数的goroutine中调用。
// 
// 其他报告方法，如日志和错误的变化，
// 可以从多个goroutine同时调用。
type T struct {
	common
	isParallel bool
	isEnvSet   bool
	context    *testContext // 用于运行测试和子测试。
}

func (c *common) private() {}

// Name返回运行的名称（sub-）测试或基准测试。
// 
// 名称将包括测试名称以及
// 任何嵌套子测试的名称。如果两个同级子测试具有相同的名称，
// 名称将附加后缀以保证返回的名称唯一。
func (c *common) Name() string {
	return c.name
}

func (c *common) setRan() {
	if c.parent != nil {
		c.parent.setRan()
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	c.ran = true
}

// 将函数标记为失败但仍在继续执行。需要锁定c.done以将检查同步到父测试中的c.done。失败报告函数是否失败。
func (c *common) Fail() {
	if c.parent != nil {
		c.parent.Fail()
	}
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.done {
		panic("Fail in goroutine after " + c.name + " has completed")
	}
	c.failed = true
}

func (c *common) Failed() bool {
	c.mu.RLock()
	failed := c.failed
	c.mu.RUnlock()
	return failed || c.raceErrors+race.Errors() > 0
}

// FailNow通过调用runtime.Goexit将函数标记为失败并停止其执行（然后运行
// 当前goroutine中的所有延迟调用）.
// 将在下一个测试或基准测试时继续执行。
// 必须从运行
// 测试或基准测试函数的goroutine调用FailNow，而不是从测试期间创建的其他goroutine调用。调用FailNow不会停止
// 这些其他goroutine。
func (c *common) FailNow() {
	c.Fail()

	// 调用runtime.Goexit将退出goroutine，该goroutine将运行此goroutine中的延迟函数，该goroutine将最终运行tRunner中的延迟行，该goroutine将向测试循环发出信号，表明此测试已完成。
	// 
	// A此代码的早期版本为：
	// 
	// c.duration=…
	// c.signal<-c.self 
	// runtime.Goexit（）
	// 
	// 此早期版本重复了代码（这些行在
	// tRunner中），但更糟糕的是运行时中隐含的goroutine teardown 
	// Goexit不保证在测试退出之前完成
	// 如果测试延迟了重要的清理
	// 函数（如删除临时文件），无法保证
	// 它会在测试失败时运行。因为我们在
	// 期间发送c.signal，现在我们知道发送
	// 只会在任何其他堆栈延迟完成后发生。
	c.mu.Lock()
	c.finished = true
	c.mu.Unlock()
	runtime.Goexit()
}

// 日志生成输出。它总是在相同的位置tack depth.
func (c *common) log(s string) {
	c.logDepth(s, 3) // logDepth+log+公共函数
}

// logDepth以任意堆栈深度生成输出。
func (c *common) logDepth(s string, depth int) {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.done {
		// 此测试已完成。请尝试将此消息
		// 记录到我们的父级。如果没有父级，请惊慌。
		for parent := c.parent; parent != nil; parent = parent.parent {
			parent.mu.Lock()
			defer parent.mu.Unlock()
			if !parent.done {
				parent.output = append(parent.output, parent.decorate(s, depth+1)...)
				return
			}
		}
		panic("Log in goroutine after " + c.name + " has completed: " + s)
	} else {
		if c.chatty != nil {
			if c.bench {
				// 基准测试不打印==CONT，所以我们应该跳过测试
				// 打印机，直接打印到标准输出。
				fmt.Print(c.decorate(s, depth+1))
			} else {
				c.chatty.Printf(c.name, "%s", c.decorate(s, depth+1))
			}

			return
		}
		c.output = append(c.output, c.decorate(s, depth+1)...)
	}
}

// 日志使用默认格式设置参数格式，类似于Println，
// 并将文本记录在错误日志中。对于测试，仅当
// 测试失败或-test.v标志i时，才会打印文本对于基准测试，文本始终为
// 打印以避免性能取决于-test.v flag.
func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }

// Logf根据格式格式化其参数，类似于Printf，而
// 将文本记录在错误日志中。如果未提供，则添加最终换行符。对于
// 测试，仅当测试失败或-test.v标志为
// 设置时，才会打印文本。对于基准测试，始终打印文本以避免性能
// 取决于-test.v标志的值。
func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) }

// 错误相当于日志后跟Fail。
func (c *common) Error(args ...interface{}) {
	c.log(fmt.Sprintln(args...))
	c.Fail()
}

// Errorf等于Logf，后跟Fail。
func (c *common) Errorf(format string, args ...interface{}) {
	c.log(fmt.Sprintf(format, args...))
	c.Fail()
}

// 致命等同于日志后跟FailNow。
func (c *common) Fatal(args ...interface{}) {
	c.log(fmt.Sprintln(args...))
	c.FailNow()
}

// Fatalf等同于Logf，后跟FailNow。
func (c *common) Fatalf(format string, args ...interface{}) {
	c.log(fmt.Sprintf(format, args...))
	c.FailNow()
}

// Skip相当于Log后跟SkipNow。
func (c *common) Skip(args ...interface{}) {
	c.log(fmt.Sprintln(args...))
	c.SkipNow()
}

// Skipf等同于Logf，后跟SkipNow。
func (c *common) Skipf(format string, args ...interface{}) {
	c.log(fmt.Sprintf(format, args...))
	c.SkipNow()
}

// SkipNow通过调用runtime.Goexit将测试标记为已跳过并停止其执行。
// 如果一个测试失败（请参阅Error，Errorf，Fail），然后被跳过，
// 它仍然被视为失败。
// 执行将在下一个测试或基准测试中继续。另见下文。必须从运行测试的goroutine调用SkipNow，而不是从测试期间创建的其他goroutine调用SkipNow。调用SkipNow并不能阻止
// 其他goroutine。
func (c *common) SkipNow() {
	c.mu.Lock()
	c.skipped = true
	c.finished = true
	c.mu.Unlock()
	runtime.Goexit()
}

// 跳过报告是否跳过了测试。
func (c *common) Skipped() bool {
	c.mu.RLock()
	defer c.mu.RUnlock()
	return c.skipped
}

// Helper将调用函数标记为测试Helper函数。
// 打印文件和行信息时，将跳过该功能。
// 可以从多个goroutine同时调用Helper。
func (c *common) Helper() {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.helperPCs == nil {
		c.helperPCs = make(map[uintptr]struct{})
	}
	// 在此处重复callerName中的代码以保存遍历堆栈帧
	var pc [1]uintptr
	n := runtime.Callers(2, pc[:]) // 跳过运行时。调用者+助手
	if n == 0 {
		panic("testing: zero callers found")
	}
	if _, found := c.helperPCs[pc[0]]; !found {
		c.helperPCs[pc[0]] = struct{}{}
		c.helperNames = nil // 下次需要时将重新创建映射
	}
}

// 清除注册一个函数，以便在测试（或子测试）及其所有
// 子测试完成时调用。清理函数将在最后添加的
// 第一次调用顺序中调用。
func (c *common) Cleanup(f func()) {
	var pc [maxStackLen]uintptr
	// 跳过两个额外的帧以说明此函数和运行时。调用方本身。
	n := runtime.Callers(2, pc[:])
	cleanupPc := pc[:n]

	fn := func() {
		defer func() {
			c.mu.Lock()
			defer c.mu.Unlock()
			c.cleanupName = ""
			c.cleanupPc = nil
		}()

		name := callerName(0)
		c.mu.Lock()
		c.cleanupName = name
		c.cleanupPc = cleanupPc
		c.mu.Unlock()

		f()
	}

	c.mu.Lock()
	defer c.mu.Unlock()
	c.cleanups = append(c.cleanups, fn)
}

// TempDir返回一个临时目录供测试使用。
// 当测试和
// 其所有子测试完成时，清理会自动删除该目录。
// 对t.TempDir的每个后续调用都返回一个唯一的目录；
// 如果目录创建失败，TempDir将通过调用Fatal终止测试。
func (c *common) TempDir() string {
	// 对测试创建的所有临时目录使用单亲目录
	// 每个临时目录按顺序编号。
	c.tempDirMu.Lock()
	var nonExistent bool
	if c.tempDir == "" { // 通常情况下，js/wasm 
		nonExistent = true
	} else {
		_, err := os.Stat(c.tempDir)
		nonExistent = os.IsNotExist(err)
		if err != nil && !nonExistent {
			c.Fatalf("TempDir: %v", err)
		}
	}

	if nonExistent {
		c.Helper()

		// 将不寻常的字符（如路径分隔符或
		// 与全局交互的字符）从目录名中删除到
		// 避免意外的os.MkdirTemp行为。
		mapper := func(r rune) rune {
			if r < utf8.RuneSelf {
				const allowed = "!#$%&()+,-.=@^_{}~ "
				if '0' <= r && r <= '9' ||
					'a' <= r && r <= 'z' ||
					'A' <= r && r <= 'Z' {
					return r
				}
				if strings.ContainsRune(allowed, r) {
					return r
				}
			} else if unicode.IsLetter(r) || unicode.IsNumber(r) {
				return r
			}
			return -1
		}
		pattern := strings.Map(mapper, c.Name())
		c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern)
		if c.tempDirErr == nil {
			c.Cleanup(func() {
				if err := os.RemoveAll(c.tempDir); err != nil {
					c.Errorf("TempDir RemoveAll cleanup: %v", err)
				}
			})
		}
	}
	c.tempDirMu.Unlock()

	if c.tempDirErr != nil {
		c.Fatalf("TempDir: %v", c.tempDirErr)
	}
	seq := atomic.AddInt32(&c.tempDirSeq, 1)
	dir := fmt.Sprintf("%s%c%03d", c.tempDir, os.PathSeparator, seq)
	if err := os.Mkdir(dir, 0777); err != nil {
		c.Fatalf("TempDir: %v", err)
	}
	return dir
}

// Setenv调用os.Setenv（key，value）并使用Cleanup来
// 在测试后将环境变量还原为其原始值
// 。
// 
// 这不能用于并行测试。
func (c *common) Setenv(key, value string) {
	prevValue, ok := os.LookupEnv(key)

	if err := os.Setenv(key, value); err != nil {
		c.Fatalf("cannot set environment variable: %v", err)
	}

	if ok {
		c.Cleanup(func() {
			os.Setenv(key, prevValue)
		})
	} else {
		c.Cleanup(func() {
			os.Unsetenv(key)
		})
	}
}

// runCleanup是一个参数。
type panicHandling int

const (
	normalPanic panicHandling = iota
	recoverAndReturnPanic
)

// 在测试结束时调用runCleanup。
// 如果catchPanic为true，则将捕获panic，并返回恢复的
// 值（如果有）。
func (c *common) runCleanup(ph panicHandling) (panicVal interface{}) {
	if ph == recoverAndReturnPanic {
		defer func() {
			panicVal = recover()
		}()
	}

	// 确保如果某个清理函数崩溃，
	// 我们仍然运行其余的清理函数。
	defer func() {
		c.mu.Lock()
		recur := len(c.cleanups) > 0
		c.mu.Unlock()
		if recur {
			c.runCleanup(normalPanic)
		}
	}()

	for {
		var cleanup func()
		c.mu.Lock()
		if len(c.cleanups) > 0 {
			last := len(c.cleanups) - 1
			cleanup = c.cleanups[last]
			c.cleanups = c.cleanups[:last]
		}
		c.mu.Unlock()
		if cleanup == nil {
			return nil
		}
		cleanup()
	}
}

// callerName在跳过帧（其中0表示当前函数）后为调用方提供函数名（用包路径限定）。
func callerName(skip int) string {
	var pc [1]uintptr
	n := runtime.Callers(skip+2, pc[:]) // 跳过+运行时。调用者+调用者名称
	if n == 0 {
		panic("testing: zero callers found")
	}
	return pcToName(pc[0])
}

func pcToName(pc uintptr) string {
	pcs := []uintptr{pc}
	frames := runtime.CallersFrames(pcs)
	frame, _ := frames.Next()
	return frame.Function
}

// 并行表示此测试将与（且仅与）
// 其他并行测试并行运行。当一个测试由于使用
// /-test.count或-test.cpu而多次运行时，单个测试的多个实例不会在
// /中并行运行。
func (t *T) Parallel() {
	if t.isParallel {
		panic("testing: t.Parallel called multiple times")
	}
	if t.isEnvSet {
		panic("testing: t.Parallel called after t.Setenv; cannot set environment variables in parallel tests")
	}
	t.isParallel = true

	// 我们不希望在测试持续时间中包含等待串行测试的时间。记录到目前为止经过的时间，然后重置
	// 计时器。
	t.duration += time.Since(t.start)

	// 添加到将由母公司发布的测试列表中。
	t.parent.sub = append(t.parent.sub, t)
	t.raceErrors += race.Errors()

	if t.chatty != nil {
		// 不幸的是，即使暂停表示命名测试*不再运行
		// 了，cmd/test2json仍将其解释为更改活动测试
		// 以便进行日志解析。我们可以修复cmd/test2json，但是
		// 无法修复已经在shell 
		// cmd/test2json的较旧版本-因此，目前仅修复cmd/test2json 
		// 是不够的。
		t.chatty.Updatef(t.name, "=== PAUSE %s\n", t.name)
	}

	t.signal <- true   // 释放调用测试。
	<-t.parent.barrier // 等待父测试完成。
	t.context.waitParallel()

	if t.chatty != nil {
		t.chatty.Updatef(t.name, "=== CONT  %s\n", t.name)
	}

	t.start = time.Now()
	t.raceErrors += -race.Errors()
}

// Setenv调用os.Setenv（key，value）并使用Cleanup将环境变量恢复为测试后的原始值
// 。
// 
// 这不能用于并行测试。
func (t *T) Setenv(key, value string) {
	if t.isParallel {
		panic("testing: t.Setenv called after t.Parallel; cannot set environment variables in parallel tests")
	}

	t.isEnvSet = true

	t.common.Setenv(key, value)
}

// InternalTest是内部类型，但由于是跨包的，所以被导出；
// 它是“go test”命令实现的一部分。
type InternalTest struct {
	Name string
	F    func(*T)
}

var errNilPanicOrGoexit = errors.New("test executed panic(nil) or runtime.Goexit")

func tRunner(t *T, fn func(t *T)) {
	t.runner = callerName(0)

	// 当此goroutine完成时，可能是因为fn（t）
	// 返回正常，或者是因为测试失败触发了对runtime.Goexit的调用，请记录持续时间并发送
	// 一个信号，表示测试已完成。
	defer func() {
		if t.Failed() {
			atomic.AddUint32(&numFailed, 1)
		}

		if t.raceErrors+race.Errors() > 0 {
			t.Errorf("race detected during execution of test")
		}

		// 如果测试恐慌，在死亡之前打印任何测试输出。
		err := recover()
		signal := true

		t.mu.RLock()
		finished := t.finished
		t.mu.RUnlock()
		if !finished && err == nil {
			err = errNilPanicOrGoexit
			for p := t.parent; p != nil; p = p.parent {
				p.mu.RLock()
				finished = p.finished
				p.mu.RUnlock()
				if finished {
					t.Errorf("%v: subtest may have called FailNow on a parent test", err)
					err = nil
					signal = false
					break
				}
			}
		}
		// 使用延迟调用确保我们报告测试是
		// 完成的，即使清除函数调用t.FailNow。见第41355期。
		didPanic := false
		defer func() {
			if didPanic {
				return
			}
			if err != nil {
				panic(err)
			}
			// 只有在没有死机的情况下才报告测试完成，否则测试二进制文件可以在死机报告给用户之前退出。见第41479期。
			t.signal <- signal
		}()

		doPanic := func(err interface{}) {
			t.Fail()
			if r := t.runCleanup(recoverAndReturnPanic); r != nil {
				t.Logf("cleanup panicked with %v", r)
			}
			// 在死之前将输出日志刷新到根目录。
			for root := &t.common; root.parent != nil; root = root.parent {
				root.mu.Lock()
				root.duration += time.Since(root.start)
				d := root.duration
				root.mu.Unlock()
				root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
				if r := root.parent.runCleanup(recoverAndReturnPanic); r != nil {
					fmt.Fprintf(root.parent.w, "cleanup panicked with %v", r)
				}
			}
			didPanic = true
			panic(err)
		}
		if err != nil {
			doPanic(err)
		}

		t.duration += time.Since(t.start)

		if len(t.sub) > 0 {
			// 运行并行子测试。
			// 减少此测试的运行计数。
			t.context.release()
			// 释放并行子测试。
			close(t.barrier)
			// 等待子测试完成。
			for _, sub := range t.sub {
				<-sub.signal
			}
			cleanupStart := time.Now()
			err := t.runCleanup(recoverAndReturnPanic)
			t.duration += time.Since(cleanupStart)
			if err != nil {
				doPanic(err)
			}
			if !t.isParallel {
				// 重新获取顺序测试的计数。请参阅Run中的注释。
				t.context.waitParallel()
			}
		} else if t.isParallel {
			// 仅当此测试作为并行
			// 测试运行时，才释放此测试的计数。请参阅Run方法中的注释。
			t.context.release()
		}
		t.report() // 完成所有子测试后报告。

		// 不要锁定t.done以允许种族检测器在用户未正确同步goroutine的情况下检测种族。
		t.done = true
		if t.parent != nil && atomic.LoadInt32(&t.hasSub) == 0 {
			t.setRan()
		}
	}()
	defer func() {
		if len(t.sub) == 0 {
			t.runCleanup(normalPanic)
		}
	}()

	t.start = time.Now()
	t.raceErrors = -race.Errors()
	fn(t)

	// 调用FailNow时，将不执行此处以外的代码
	t.mu.Lock()
	t.finished = true
	t.mu.Unlock()
}

// 运行f作为名为name的t的子测试。它在一个单独的goroutine 
// 中运行f，并阻塞，直到f返回或调用t.Parallel成为并行测试。
// Run报告f是否成功（或者至少在调用t.Parallel之前没有失败）。
// 
// 可以从多个goroutine同时调用Run，但所有此类调用
// 必须在t的外部测试函数返回之前返回。
func (t *T) Run(name string, f func(t *T)) bool {
	atomic.StoreInt32(&t.hasSub, 1)
	testName, ok, _ := t.context.match.fullName(&t.common, name)
	if !ok || shouldFailFast() {
		return true
	}
	// 记录此调用点的堆栈跟踪，以便如果在单独堆栈中运行的子测试
	// 函数标记为帮助程序，我们可以
	// 继续将堆栈遍历到父测试中。
	var pc [maxStackLen]uintptr
	n := runtime.Callers(2, pc[:])
	t = &T{
		common: common{
			barrier: make(chan bool),
			signal:  make(chan bool, 1),
			name:    testName,
			parent:  &t.common,
			level:   t.level + 1,
			creator: pc[:n],
			chatty:  t.chatty,
		},
		context: t.context,
	}
	t.w = indenter{&t.common}

	if t.chatty != nil {
		t.chatty.Updatef(t.name, "=== RUN   %s\n", t.name)
	}
	// 在调用
	// tRunner之前并在之后增加它，我们依靠tRunner保持
	// 计数正确。这确保了一系列顺序测试运行
	// 而不会被抢占，即使它们的父测试是并行测试。如果*parallel==1，这个
	// 可能会特别减少惊喜。
	go tRunner(t, f)
	if !<-t.signal {
		// 此时，可能是其中一个子测试对其中一个
		// 父测试调用了FailNow。继续中止该链。
		runtime.Goexit()
	}
	return !t.failed
}

// 截止日期报告测试二进制文件超过-timeout标志指定的超时的时间。
// 
// 如果-timeout标志指示“无超时”（0），则ok结果为false。
func (t *T) Deadline() (deadline time.Time, ok bool) {
	deadline = t.context.deadline
	return deadline, !deadline.IsZero()
}

// testContext保存所有测试通用的所有字段。这包括
// 最多运行*并行测试的同步原语。
type testContext struct {
	match    *matcher
	deadline time.Time

	mu sync.Mutex

	// 用于向准备并行运行的测试发送信号的通道。
	startParallel chan bool

	// running是当前并行运行的测试数。
	// 这不包括等待子测试完成的测试。
	running int

	// numWaiting是等待并行运行的测试数。
	numWaiting int

	// maxParallel是parallel标志的副本。
	maxParallel int
}

func newTestContext(maxParallel int, m *matcher) *testContext {
	return &testContext{
		match:         m,
		startParallel: make(chan bool),
		maxParallel:   maxParallel,
		running:       1, // 将主（顺序）测试的计数设置为1。
	}
}

func (c *testContext) waitParallel() {
	c.mu.Lock()
	if c.running < c.maxParallel {
		c.running++
		c.mu.Unlock()
		return
	}
	c.numWaiting++
	c.mu.Unlock()
	<-c.startParallel
}

func (c *testContext) release() {
	c.mu.Lock()
	if c.numWaiting == 0 {
		c.running--
		c.mu.Unlock()
		return
	}
	c.numWaiting--
	c.mu.Unlock()
	c.startParallel <- true // 选择要运行的等待测试。
}

// 任何人都不应该再使用func Main了。
// 查看func Main上的文档注释，改用MainStart。
var errMain = errors.New("testing: unexpected use of func Main")

type matchStringOnly func(pat, str string) (bool, error)

func (f matchStringOnly) MatchString(pat, str string) (bool, error)   { return f(pat, str) }
func (f matchStringOnly) StartCPUProfile(w io.Writer) error           { return errMain }
func (f matchStringOnly) StopCPUProfile()                             {}
func (f matchStringOnly) WriteProfileTo(string, io.Writer, int) error { return errMain }
func (f matchStringOnly) ImportPath() string                          { return "" }
func (f matchStringOnly) StartTestLog(io.Writer)                      {}
func (f matchStringOnly) StopTestLog() error                          { return errMain }
func (f matchStringOnly) SetPanicOnExit0(bool)                        {}

// Main是一个内部函数，是执行“go test”命令的一部分。
// 它之所以被导出，是因为它是跨包的，并且早于“内部”包。
// 它不再被“go-test”使用，而是尽可能地为其他
// 使用Main模拟“go-test”的系统保留，但Main有时无法更新，因为
// 测试包中添加了新功能。
// 模拟“go test”的系统应更新为使用Mainstrart。
func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
	os.Exit(MainStart(matchStringOnly(matchString), tests, benchmarks, examples).Run())
}

// M是传递给TestMain函数以运行实际测试的类型。
type M struct {
	deps       testDeps
	tests      []InternalTest
	benchmarks []InternalBenchmark
	examples   []InternalExample

	timer     *time.Timer
	afterOnce sync.Once

	numRun int

	// 要传递给os.Exit的值，外部测试函数main 
	// 线束使用以下代码调用os.Exit。见第34129页。
	exitCode int
}

// testDeps是由测试生成的主包传入此包的功能的内部接口。
// 此接口的规范实现是
// testing/internal/testdeps的testdeps。
type testDeps interface {
	ImportPath() string
	MatchString(pat, str string) (bool, error)
	SetPanicOnExit0(bool)
	StartCPUProfile(io.Writer) error
	StopCPUProfile()
	StartTestLog(io.Writer)
	StopTestLog() error
	WriteProfileTo(string, io.Writer, int) error
}

// MainStart用于“go test”生成的测试。
// 它不打算直接调用，也不受Go 1兼容性文档的约束。
// 它可能会在不同版本之间更改签名。
func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
	Init()
	return &M{
		deps:       deps,
		tests:      tests,
		benchmarks: benchmarks,
		examples:   examples,
	}
}

// 运行测试。它返回一个要传递给os.exit的退出代码。
func (m *M) Run() (code int) {
	defer func() {
		code = m.exitCode
	}()

	// 计算m.Run的调用数。
	// 我们只期望1，但我们没有强制执行，
	// 现在有一些测试在野外调用m.运行多次。
	// 叹气。golang.org/issue/23129。
	m.numRun++

	// TestMain可能已经调用了flag.Parse。
	if !flag.Parsed() {
		flag.Parse()
	}

	if *parallel < 1 {
		fmt.Fprintln(os.Stderr, "testing: -parallel can only be given a positive integer")
		flag.Usage()
		m.exitCode = 2
		return
	}

	if len(*matchList) != 0 {
		listTests(m.deps.MatchString, m.tests, m.benchmarks, m.examples)
		m.exitCode = 0
		return
	}

	if *shuffle != "off" {
		var n int64
		var err error
		if *shuffle == "on" {
			n = time.Now().UnixNano()
		} else {
			n, err = strconv.ParseInt(*shuffle, 10, 64)
			if err != nil {
				fmt.Fprintln(os.Stderr, `testing: -shuffle should be "off", "on", or a valid integer:`, err)
				m.exitCode = 2
				return
			}
		}
		fmt.Println("-test.shuffle", n)
		rng := rand.New(rand.NewSource(n))
		rng.Shuffle(len(m.tests), func(i, j int) { m.tests[i], m.tests[j] = m.tests[j], m.tests[i] })
		rng.Shuffle(len(m.benchmarks), func(i, j int) { m.benchmarks[i], m.benchmarks[j] = m.benchmarks[j], m.benchmarks[i] })
	}

	parseCpuList()

	m.before()
	defer m.after()
	deadline := m.startAlarm()
	haveExamples = len(m.examples) > 0
	testRan, testOk := runTests(m.deps.MatchString, m.tests, deadline)
	exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
	m.stopAlarm()
	if !testRan && !exampleRan && *matchBenchmarks == "" {
		fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
	}
	if !testOk || !exampleOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
		fmt.Println("FAIL")
		m.exitCode = 1
		return
	}

	fmt.Println("PASS")
	m.exitCode = 0
	return
}

func (t *T) report() {
	if t.parent == nil {
		return
	}
	dstr := fmtDuration(t.duration)
	format := "--- %s: %s (%s)\n"
	if t.Failed() {
		t.flushToParent(t.name, format, "FAIL", t.name, dstr)
	} else if t.chatty != nil {
		if t.Skipped() {
			t.flushToParent(t.name, format, "SKIP", t.name, dstr)
		} else {
			t.flushToParent(t.name, format, "PASS", t.name, dstr)
		}
	}
}

func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
	if _, err := matchString(*matchList, "non-empty"); err != nil {
		fmt.Fprintf(os.Stderr, "testing: invalid regexp in -test.list (%q): %s\n", *matchList, err)
		os.Exit(1)
	}

	for _, test := range tests {
		if ok, _ := matchString(*matchList, test.Name); ok {
			fmt.Println(test.Name)
		}
	}
	for _, bench := range benchmarks {
		if ok, _ := matchString(*matchList, bench.Name); ok {
			fmt.Println(bench.Name)
		}
	}
	for _, example := range examples {
		if ok, _ := matchString(*matchList, example.Name); ok {
			fmt.Println(example.Name)
		}
	}
}

// RunTests是一个内部函数，但由于它是跨包的，所以被导出；
// 它是“go test”命令实现的一部分。
func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool) {
	var deadline time.Time
	if *timeout > 0 {
		deadline = time.Now().Add(*timeout)
	}
	ran, ok := runTests(matchString, tests, deadline)
	if !ran && !haveExamples {
		fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
	}
	return ok
}

func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest, deadline time.Time) (ran, ok bool) {
	ok = true
	for _, procs := range cpuList {
		runtime.GOMAXPROCS(procs)
		for i := uint(0); i < *count; i++ {
			if shouldFailFast() {
				break
			}
			ctx := newTestContext(*parallel, newMatcher(matchString, *match, "-test.run"))
			ctx.deadline = deadline
			t := &T{
				common: common{
					signal:  make(chan bool, 1),
					barrier: make(chan bool),
					w:       os.Stdout,
				},
				context: ctx,
			}
			if Verbose() {
				t.chatty = newChattyPrinter(t.w)
			}
			tRunner(t, func(t *T) {
				for _, test := range tests {
					t.Run(test.Name, test.F)
				}
			})
			select {
			case <-t.signal:
			default:
				panic("internal error: tRunner exited without sending on t.signal")
			}
			ok = ok && !t.Failed()
			ran = ran || t.ran
		}
	}
	return ran, ok
}

// 在所有测试之前运行。
func (m *M) before() {
	if *memProfileRate > 0 {
		runtime.MemProfileRate = *memProfileRate
	}
	if *cpuProfile != "" {
		f, err := os.Create(toOutputDir(*cpuProfile))
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: %s\n", err)
			return
		}
		if err := m.deps.StartCPUProfile(f); err != nil {
			fmt.Fprintf(os.Stderr, "testing: can't start cpu profile: %s\n", err)
			f.Close()
			return
		}
		// 可以保存f，以便在调用f.Close后可以进行保存；不值得努力。
	}
	if *traceFile != "" {
		f, err := os.Create(toOutputDir(*traceFile))
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: %s\n", err)
			return
		}
		if err := trace.Start(f); err != nil {
			fmt.Fprintf(os.Stderr, "testing: can't start tracing: %s\n", err)
			f.Close()
			return
		}
		// 可以保存f，以便在调用f.Close后可以进行保存；不值得努力。
	}
	if *blockProfile != "" && *blockProfileRate >= 0 {
		runtime.SetBlockProfileRate(*blockProfileRate)
	}
	if *mutexProfile != "" && *mutexProfileFraction >= 0 {
		runtime.SetMutexProfileFraction(*mutexProfileFraction)
	}
	if *coverProfile != "" && cover.Mode == "" {
		fmt.Fprintf(os.Stderr, "testing: cannot use -test.coverprofile because test binary was not built with coverage enabled\n")
		os.Exit(2)
	}
	if *testlog != "" {
		// 注意：不使用toOutputDir。
		// 此文件供cmd/go使用，而不是供用户使用。
		var f *os.File
		var err error
		if m.numRun == 1 {
			f, err = os.Create(*testlog)
		} else {
			f, err = os.OpenFile(*testlog, os.O_WRONLY, 0)
			if err == nil {
				f.Seek(0, io.SeekEnd)
			}
		}
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: %s\n", err)
			os.Exit(2)
		}
		m.deps.StartTestLog(f)
		testlogFile = f
	}
	if *panicOnExit0 {
		m.deps.SetPanicOnExit0(true)
	}
}

// 在所有测试后运行。
func (m *M) after() {
	m.afterOnce.Do(func() {
		m.writeProfiles()
	})

	// 在每次运行后恢复PanicOnExit0，因为我们在每次运行前将其设置为true。否则，如果多次调用m.Run，则在第二次运行后将不会恢复
	// os.Exit（0）的行为。
	if *panicOnExit0 {
		m.deps.SetPanicOnExit0(false)
	}
}

func (m *M) writeProfiles() {
	if *testlog != "" {
		if err := m.deps.StopTestLog(); err != nil {
			fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *testlog, err)
			os.Exit(2)
		}
		if err := testlogFile.Close(); err != nil {
			fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *testlog, err)
			os.Exit(2)
		}
	}
	if *cpuProfile != "" {
		m.deps.StopCPUProfile() // 将配置文件刷新到磁盘
	}
	if *traceFile != "" {
		trace.Stop() // 将跟踪刷新到磁盘
	}
	if *memProfile != "" {
		f, err := os.Create(toOutputDir(*memProfile))
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: %s\n", err)
			os.Exit(2)
		}
		runtime.GC() // 实现所有统计
		if err = m.deps.WriteProfileTo("allocs", f, 0); err != nil {
			fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *memProfile, err)
			os.Exit(2)
		}
		f.Close()
	}
	if *blockProfile != "" && *blockProfileRate >= 0 {
		f, err := os.Create(toOutputDir(*blockProfile))
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: %s\n", err)
			os.Exit(2)
		}
		if err = m.deps.WriteProfileTo("block", f, 0); err != nil {
			fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *blockProfile, err)
			os.Exit(2)
		}
		f.Close()
	}
	if *mutexProfile != "" && *mutexProfileFraction >= 0 {
		f, err := os.Create(toOutputDir(*mutexProfile))
		if err != nil {
			fmt.Fprintf(os.Stderr, "testing: %s\n", err)
			os.Exit(2)
		}
		if err = m.deps.WriteProfileTo("mutex", f, 0); err != nil {
			fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *mutexProfile, err)
			os.Exit(2)
		}
		f.Close()
	}
	if cover.Mode != "" {
		coverReport()
	}
}

// toOutputDir返回重新定位到outputDir的文件名（如果需要）。
// 简单的实现避免拉入path/filepath。
func toOutputDir(path string) string {
	if *outputDir == "" || path == "" {
		return path
	}
	// 在Windows上，这很笨拙，但只要查找驱动器号和冒号，我们几乎总能纠正
	// 的错误。
	// 绝对路径始终有一个驱动器号（忽略UNC）。
	// 问题：如果path==“C:A”和outputdir==“C:\Go”，则不清楚该怎么办，但即使这样，path/filepath也没有帮助。
	// TODO:值得做得更好吗？可能不会，因为我们这里只有
	// 在go测试的管理下。
	if runtime.GOOS == "windows" && len(path) >= 2 {
		letter, colon := path[0], path[1]
		if ('a' <= letter && letter <= 'z' || 'A' <= letter && letter <= 'Z') && colon == ':' {
			// 如果路径以驱动器号开头，则不管怎样，我们都会使用它。
			return path
		}
	}
	if os.IsPathSeparator(path[0]) {
		return path
	}
	return fmt.Sprintf("%s%c%s", *outputDir, os.PathSeparator, path)
}

// StartArm会根据请求启动警报。
func (m *M) startAlarm() time.Time {
	if *timeout <= 0 {
		return time.Time{}
	}

	deadline := time.Now().Add(*timeout)
	m.timer = time.AfterFunc(*timeout, func() {
		m.after()
		debug.SetTraceback("all")
		panic(fmt.Sprintf("test timed out after %v", *timeout))
	})
	return deadline
}

// 停止报警关闭报警。
func (m *M) stopAlarm() {
	if *timeout > 0 {
		m.timer.Stop()
	}
}

func parseCpuList() {
	for _, val := range strings.Split(*cpuListStr, ",") {
		val = strings.TrimSpace(val)
		if val == "" {
			continue
		}
		cpu, err := strconv.Atoi(val)
		if err != nil || cpu <= 0 {
			fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu\n", val)
			os.Exit(1)
		}
		cpuList = append(cpuList, cpu)
	}
	if cpuList == nil {
		cpuList = append(cpuList, runtime.GOMAXPROCS(-1))
	}
}

func shouldFailFast() bool {
	return *failFast && atomic.LoadUint32(&numFailed) > 0
}
